package com.zlsy.springbootdemo.strategy.example1.common;

import com.zlsy.springbootdemo.strategy.example1.annotations.StrategyExampleAnnotation;
import com.zlsy.springbootdemo.strategy.example1.enums.StrategyEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

import java.lang.annotation.Annotation;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author zhouliang
 * @date 2021/1/19 16:07
 */
@Slf4j
@Component
public class StrategyAnnotationFactory implements ApplicationContextAware {

  private static final Map<Class<? extends Annotation>, Map<String, Object>> IMPL_MAP =
      new ConcurrentHashMap<>();

  private ApplicationContext applicationContext;

  @Override
  public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
    this.applicationContext = applicationContext;
  }

  public <T> Object getBean(Class<T> requiredType) {
    return this.applicationContext.getBean(requiredType);
  }

  private Map<String, Object> getImplBeans(Class<? extends Annotation> annotaion) {
    Map<String, Object> beans = IMPL_MAP.get(annotaion);
    try {
      if (Objects.equals(beans, null) || beans.isEmpty()) {
        beans = applicationContext.getBeansWithAnnotation(annotaion);
        IMPL_MAP.put(annotaion, beans);
        return beans;
      }
    } catch (Exception e) {
      log.error("get implBeans exception:", e);
    }
    return beans;
  }

  @SuppressWarnings("unchecked")
  public <T> T getImpl(Class<T> t, StrategyEnum typeEnum) {
    try {
      Map<String, Object> implBeans = getImplBeans(StrategyExampleAnnotation.class);
      Set<Map.Entry<String, Object>> entries = implBeans.entrySet();
      for (Map.Entry<String, Object> next : entries) {
        Object obj = next.getValue();
        StrategyExampleAnnotation annotation =
            obj.getClass().getAnnotation(StrategyExampleAnnotation.class);
        if (annotation.value().equals(typeEnum)) {
          return (T) obj;
        }
      }
    } catch (Exception e) {
      log.error(e.getMessage(), e);
    }
    return null;
  }
}
